package com.ryan.security.core.validate.code;

import com.ryan.security.core.properties.SecurityProperties;
import lombok.Data;
import org.apache.commons.lang.StringUtils;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.security.web.authentication.AuthenticationFailureHandler;
import org.springframework.social.connect.web.HttpSessionSessionStrategy;
import org.springframework.social.connect.web.SessionStrategy;
import org.springframework.util.AntPathMatcher;
import org.springframework.web.bind.ServletRequestBindingException;
import org.springframework.web.bind.ServletRequestUtils;
import org.springframework.web.context.request.ServletWebRequest;
import org.springframework.web.filter.OncePerRequestFilter;

import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.HashSet;
import java.util.Set;

/**
 * Created by kaimin on 2/1/2019.
 * time : 20:49
 */
//继承之后，保证这个过滤器只会被调用一次 这个过滤器要放在UserNameAuthenticationFilter之前，在config里面加
@Data
public class ValidatorCodeFilter extends OncePerRequestFilter implements InitializingBean{

    private AuthenticationFailureHandler authenticationFailureHandler;

    private SessionStrategy sessionStrategy=new HttpSessionSessionStrategy();

    private Set<String> urls=new HashSet<>();

    private SecurityProperties securityProperties;

    private AntPathMatcher antPathMatcher=new AntPathMatcher();
    @Override
    public void afterPropertiesSet() throws ServletException {
        super.afterPropertiesSet();//属性都设置完成之后，设置urls

        //这里配置，哪些接口需要验证码
        String[] configUrls = StringUtils.splitByWholeSeparatorPreserveAllTokens(securityProperties.getCode().getImage().getUrl(),",");
        if (configUrls != null) {
            for (String url:configUrls) {
                urls.add(url);
            }
        }
        urls.add("/authentication/form");
    }

    @Override
    protected void doFilterInternal(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, FilterChain filterChain) throws ServletException, IOException {
        /*if (StringUtils.equals("/authentication/form", httpServletRequest.getRequestURI())
                && StringUtils.endsWithIgnoreCase("post", httpServletRequest.getMethod())
                )*/
        boolean action=false;
        for (String url : urls) {
            if (antPathMatcher.match(url, httpServletRequest.getRequestURI())) {
                action=true;
            }
        }
        if (action){
            //只有在登录的时候才要验证
            try {
                validate(new ServletWebRequest(httpServletRequest));
            } catch (ValidatorCodeException e) {
                //用自定义的失败处理器处理
                authenticationFailureHandler.onAuthenticationFailure(httpServletRequest,httpServletResponse,e);
                //这里不加return的化会继续往下走，所以加return
                return;
            }
            filterChain.doFilter(httpServletRequest,httpServletResponse);
        } else {
            //直接调用后面的过滤器
            filterChain.doFilter(httpServletRequest,httpServletResponse);
        }
    }

    private void validate(ServletWebRequest request) {


        ImageCode codeInSession = (ImageCode) sessionStrategy.getAttribute(request, ValidateCodeProcessor.SESSION_KEY_PREFIX+"IMAGE");

        String codeInRequest;
        try {
            codeInRequest = ServletRequestUtils.getStringParameter(request.getRequest(),
                    "imageCode");
        } catch (ServletRequestBindingException e) {
            throw new ValidatorCodeException("获取验证码的值失败");
        }

        if (StringUtils.isBlank(codeInRequest)) {
            throw new ValidatorCodeException( "验证码的值不能为空");
        }

        if (codeInSession == null) {
            throw new ValidatorCodeException( "验证码不存在");
        }

        if (codeInSession.isExpried()) {
            sessionStrategy.removeAttribute(request, ValidatorController1.SESSION_KEY);
            throw new ValidatorCodeException( "验证码已过期");
        }

        if (!StringUtils.equals(codeInSession.getCode(), codeInRequest)) {
            throw new ValidatorCodeException("验证码不匹配");
        }

        sessionStrategy.removeAttribute(request, ValidatorController1.SESSION_KEY);
    }
}
